package com.wg.net.server.http;

import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.alibaba.fastjson.JSON;
import com.wg.net.NetConfig;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.io.StringWriter;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Function;

import static io.netty.handler.codec.http.HttpHeaderValues.*;

/**
 * 增强版HTTP响应对象，支持灵活序列化、CORS配置、压缩和线程安全
 *
 * @author 少爷123
 */
public class HttpServletResponse {
    private static final Log log = LogFactory.get();
    /**
     * 线程安全的响应头存储（支持动态添加自定义头）
     */
    private final Map<String, String> headers = new ConcurrentHashMap<>();
    /**
     * 通道上下文
     */
    private final ChannelHandlerContext ctx;
    /**
     * 是否保持长连接
     */
    private final boolean keepAlive;
    /**
     * 请求URL
     */
    private final String url;
    /**
     * 是否已完成
     */
    private final AtomicBoolean finished = new AtomicBoolean(false);
    /**
     * 响应状态码
     */
    private HttpResponseStatus status = HttpResponseStatus.OK;
    /**
     * 字符集
     */
    private Charset charset = StandardCharsets.UTF_8;
    /**
     * 内容类型
     */
    private String contentType = "text/plain";
    /**
     * 响应体内容
     */
    private byte[] content;
    /**
     * 序列化器注册表（键：ContentType，值：序列化函数）
     */
    private static final Map<String, Function<Object, byte[]>> SERIALIZERS = new ConcurrentHashMap<>();
    /**
     * CORS配置（从全局配置加载）
     */
    private final CorsConfig corsConfig;

    static {
        // 初始化默认序列化器
        registerSerializer("text/plain", obj -> obj.toString().getBytes(StandardCharsets.UTF_8));
        registerSerializer("application/json", obj -> JSON.toJSONString(obj).getBytes(StandardCharsets.UTF_8));
        registerSerializer("text/xml", obj -> {
            try {
                return HttpServletResponse.serializeToXml(obj);
            } catch (JAXBException e) {
                log.error("XML序列化失败", e);
                return null;

            }
        });
        registerSerializer("application/xml", obj -> {
            try {
                return HttpServletResponse.serializeToXml(obj);
            } catch (JAXBException e) {
                log.error("XML序列化失败", e);
                return null;
            }
        });
        registerSerializer("application/octet-stream", obj -> (byte[]) obj);
    }

    /**
     * 构造响应对象
     *
     * @param ctx        Netty通道上下文
     * @param keepAlive  是否保持长连接
     * @param url        请求URL
     * @param corsConfig CORS配置（允许的源、方法等）
     */
    public HttpServletResponse(ChannelHandlerContext ctx, boolean keepAlive, String url, CorsConfig corsConfig) {
        this.ctx = ctx;
        this.keepAlive = keepAlive;
        this.url = url;
        this.corsConfig = corsConfig != null ? corsConfig : new CorsConfig(); // 默认允许所有源
        initDefaultHeaders();
    }

    /**
     * 初始化默认响应头
     */
    private void initDefaultHeaders() {
        headers.put(HttpHeaderNames.SERVER.toString(), "WG-HTTP-Server/3.4");
        headers.put(HttpHeaderNames.DATE.toString(), Instant.now().toString());
        headers.put(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN.toString(),
                String.join(",", corsConfig.getAllowedOrigins()));
        headers.put(HttpHeaderNames.ACCESS_CONTROL_ALLOW_HEADERS.toString(),
                String.join(",", corsConfig.getAllowedHeaders()));
        headers.put(HttpHeaderNames.ACCESS_CONTROL_ALLOW_METHODS.toString(),
                String.join(",", corsConfig.getAllowedMethods()));
        headers.put(HttpHeaderNames.ACCESS_CONTROL_ALLOW_CREDENTIALS.toString(),
                corsConfig.isAllowCredentials() ? "true" : "false");
    }

    public String getContentType() {
        return contentType;
    }

    public void setContentType(String contentType) {
        this.contentType = contentType;
    }

    public Charset getCharset() {
        return charset;
    }

    public void setCharset(Charset charset) {
        this.charset = charset != null ? charset : StandardCharsets.UTF_8;
    }

    public HttpResponseStatus getStatus() {
        return status;
    }

    public void setStatus(HttpResponseStatus status) {
        this.status = status != null ? status : HttpResponseStatus.INTERNAL_SERVER_ERROR;
    }

    public byte[] getContent() {
        return content != null ? content.clone() : null;
    }

    /**
     * 写入字符串内容
     */
    public void writeString(String str) {
        if (str == null) {
            this.content = "null".getBytes(charset);
            return;
        }
        this.content = str.getBytes(charset);
    }

    /**
     * 写入对象（自动根据contentType选择序列化方式）
     */
    public void writeObject(Object obj) {
        if (obj == null) {
            this.content = "null".getBytes(charset);
            return;
        }

        try {
            Function<Object, byte[]> serializer = SERIALIZERS.getOrDefault(contentType, this::defaultSerialize);
            this.content = serializer.apply(obj);
        } catch (Exception e) {
            log.error("对象序列化失败 | 类型: {}, Content-Type: {}", obj.getClass().getName(), contentType, e);
            this.content = ("序列化失败: " + e.getMessage()).getBytes(charset);
            setStatus(HttpResponseStatus.INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * 默认序列化（兼容未知类型）
     */
    private byte[] defaultSerialize(Object obj) {
        if (obj instanceof byte[]) {
            return (byte[]) obj;
        }
        return JSON.toJSONString(obj).getBytes(charset);
    }

    /**
     * 写入错误响应
     */
    public void writeError(HttpResponseStatus responseStatus, String msg) {
        this.status = responseStatus;
        writeString(msg != null ? msg : responseStatus.reasonPhrase());
    }

    /**
     * 刷新并发送响应（仅能调用一次）
     */
    public void flush() {
        if (!finished.compareAndSet(false, true)) {
            log.warn("重复刷新响应 | URL: {}", url);
            return;
        }

        try {
            byte[] finalContent = this.content != null ? this.content : new byte[0];
            boolean gzip = shouldCompress(finalContent.length);

            // 处理压缩
            if (gzip) {
                finalContent = ZipUtil.gzip(finalContent);
                headers.put(HttpHeaderNames.CONTENT_ENCODING.toString(), GZIP.toString());
            }

            // 构建响应对象
            FullHttpResponse response = new DefaultFullHttpResponse(
                    HttpVersion.HTTP_1_1,
                    status,
                    Unpooled.wrappedBuffer(finalContent));

            // 设置标准头
            response.headers()
                    .set(HttpHeaderNames.CONTENT_TYPE, StrUtil.format("{};charset={}", contentType, charset.name()))
                    .set(HttpHeaderNames.CONTENT_LENGTH, finalContent.length)
                    .set(HttpHeaderNames.CONNECTION, keepAlive ? KEEP_ALIVE : CLOSE);

            // 添加自定义头
            headers.forEach(response.headers()::set);

            // 发送响应并处理连接
            ChannelFuture future = ctx.writeAndFlush(response);
            if (!keepAlive) {
                future.addListener(ChannelFutureListener.CLOSE);
            }
            log.debug("响应已发送 | URL: {}, 状态: {}, 压缩: {}, 大小: {}字节",
                    url, status.code(), gzip, finalContent.length);
        } catch (Exception e) {
            log.error("发送响应失败 | URL: {}", url, e);
        }
    }

    /**
     * 快速发送对象（组合writeObject+flush）
     */
    public void writeAndFlush(Object obj) {
        writeObject(obj);
        flush();
    }

    /**
     * 快速发送字符串（组合writeString+flush）
     */
    public void writeAndFlush(String str) {
        writeString(str);
        flush();
    }

    /**
     * 快速发送错误响应（组合writeError+flush）
     */
    public void writeAndFlush(HttpResponseStatus responseStatus, String msg) {
        writeError(responseStatus, msg);
        flush();
    }

    /**
     * 注册自定义序列化器
     *
     * @param contentType 支持的Content-Type（如"application/yaml"）
     * @param serializer  序列化函数（输入对象，输出字节数组）
     */
    public static void registerSerializer(String contentType, Function<Object, byte[]> serializer) {
        if (StrUtil.isNotBlank(contentType) && serializer != null) {
            SERIALIZERS.put(contentType.toLowerCase(), serializer);
        }
    }

    /**
     * 序列化对象为XML（JAXB实现）
     */
    private static byte[] serializeToXml(Object obj) throws JAXBException {
        if (obj == null) {
            return "null".getBytes(StandardCharsets.UTF_8);
        }
        JAXBContext context = JAXBContext.newInstance(obj.getClass());
        Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        StringWriter writer = new StringWriter();
        marshaller.marshal(obj, writer);
        return writer.toString().getBytes(StandardCharsets.UTF_8);
    }

    /**
     * 判断是否需要Gzip压缩（基于配置和内容类型）
     */
    private boolean shouldCompress(int contentLength) {
        if (contentLength < NetConfig.getInstance().getGzipThreshold()) {
            return false;
        }
        String contentType = this.contentType.toLowerCase();
        return contentType.startsWith("text/")
                || contentType.startsWith("application/json")
                || contentType.startsWith("application/javascript")
                || contentType.startsWith("application/xml")
                || contentType.startsWith("application/yaml");
    }

    /**
     * 获取客户端IP（从ChannelHandlerContext）
     */
    public String getClientIp() {
        InetSocketAddress inet = (InetSocketAddress) ctx.channel().remoteAddress();
        return inet != null ? inet.getAddress().getHostAddress() : "unknown";
    }
}